package cc.shacocloud.mirage.restful.bind.support;

import cc.shacocloud.mirage.restful.AbstractMessageConverterMethodProcessor;
import cc.shacocloud.mirage.restful.HttpRequest;
import cc.shacocloud.mirage.restful.HttpResponse;
import cc.shacocloud.mirage.restful.bind.WebDataBinder;
import cc.shacocloud.mirage.restful.bind.WebDataBinderFactory;
import cc.shacocloud.mirage.restful.bind.annotation.RequestBody;
import cc.shacocloud.mirage.restful.bind.annotation.ResponseBody;
import cc.shacocloud.mirage.restful.exception.HttpMessageNotReadableException;
import cc.shacocloud.mirage.restful.http.HttpMessageConverter;
import cc.shacocloud.mirage.utils.MethodParameter;
import cc.shacocloud.mirage.utils.annotation.AnnotatedElementUtils;
import io.vertx.core.Future;
import io.vertx.core.buffer.Buffer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.List;

/**
 * 解析用 {@link RequestBody} 标注的方法参数，并通过使用{@link HttpMessageConverter}读取和写入请求或响应的主体
 * 来处理用{@link ResponseBody}标注的方法的返回值。
 */
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
    
    public RequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> messageConverters) {
        super(messageConverters);
    }
    
    @Override
    public boolean supportsParameter(@NotNull MethodParameter parameter) {
        return parameter.hasParameterAnnotation(RequestBody.class);
    }
    
    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
                returnType.hasMethodAnnotation(ResponseBody.class));
    }
    
    @Override
    public Future<Buffer> handleReturnValue(HttpResponse response, MethodParameter returnType, Object returnValue) {
        return writeWithMessageConverters(returnValue, returnType, response);
    }
    
    /**
     * 解析参数
     */
    @Override
    public Future<Object> resolveArgument(@NotNull HttpRequest request,
                                          @NotNull MethodParameter parameter,
                                          @Nullable WebDataBinderFactory binderFactory) {
        final MethodParameter finalParameter = parameter.nestedIfOptional();
        
        return readWithMessageConverters(request, finalParameter)
                .compose(arg -> {
                    String name = finalParameter.getParameterName();
                    
                    WebDataBinder binder = null;
                    if (binderFactory != null) {
                        binder = binderFactory.createBinder(request, arg, name);
                    }
                    
                    return handleResolvedValue(arg, name, binder, parameter, request);
                });
    }
    
    /**
     * 使用消息转换器进行转换消息
     *
     * @param request   当前请求对象
     * @param parameter {@link MethodParameter}
     */
    protected Future<?> readWithMessageConverters(HttpRequest request, MethodParameter parameter) {
        // 读取参数
        Future<?> argFuture = readWithMessageConverters(request, parameter, parameter.getNestedGenericParameterType());
        
        return argFuture.compose(arg -> {
            if (arg == null && checkRequired(parameter)) {
                return Future.failedFuture(new HttpMessageNotReadableException("所需的请求主体丢失: " + parameter.getExecutable().toGenericString(), request));
            }
            return Future.succeededFuture(arg);
        });
    }
    
    
    protected boolean checkRequired(MethodParameter parameter) {
        RequestBody requestBody = parameter.getParameterAnnotation(RequestBody.class);
        return (requestBody != null && requestBody.required() && !parameter.isOptional());
    }
    
}
